package util;

import org.junit.Test;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

public class BlockHeaderUtil {

//    public static void main(String[] args) throws Exception {
//
//        String version = "20000000";
//        String prevhash = "0fb597e7d687d9e764dbb344427d035ce788b3b4000209d70000000000000000";
//
//        String [] merkle_branch = {"867ceff96e6791b933d00b3f912de7fb9bb4b409b6b3471edef2463669d6b6a7","64d5ec00703792c473d6f6e1eaabddfac83bd44fe82e72ea0bf88596751eeb1a","6c92fb53571f78c00ea23c6dc86751993d99eaf02f695e578b09c222aefb430c","da117314edaeeb7cb8d0b13fee2e616bfc68f3f1137e5a97568ca21bd0f74c6c","ab9d6639621c4623ad960b987e138d5569f8a3e21bbcb13616c76e348289904a","d6fb03c4594221c096a99912a5163c5e32eab82a3e765b13237fa972ca768f07","db9ca97adadcf2b2b9a8c97be26cbbd25825b8e04e5f06349d2bc0cc26252f88","71203d3d40bd652121543663d21e2b4e35a83492960b647aea046fafd3ea2303","3b1256aed8999bebe80e8ffb10e8897974342e1fabf1fb7596781799df90e318","12c08dbce67924b11b7b1655fd47f6366575d5afe7cbb365d863e2e1273ccb3b","2a444b5ff89804323234cd7a933742c3182966c90024b6d8fca712629bd3a28c","68f663f3255c32b0f21603e46c54836b1792e94ed3dcc1c6451173c532e3123e"};
//        String ntime = "67a4bf60";
//        String nonce = "e4bf00ba";
//        String nbits = "17029a8a";
//
//        String coinb1 = "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff170381770d5075626c69632d506f6f6c";
//        String extranonce1 = "0ff71a56";
//        String extranonce2 = "00000000";
//        String coinb2 = "ffffffff02c29bc31200000000160014fe255e4cc6748570c369c2a71640280958d2376e0000000000000000266a24aa21a9edb0d498c5e0f7a245efb1218d296907b46af846ba5f96937f639496b625ae165200000000";
//
//        String coinbase = coinb1 + extranonce1 + extranonce2 + coinb2;
//
//        String merkle_root = getMerkle_root(coinbase, merkle_branch);
//
//        String headStr = version + prevhash + merkle_root + ntime + nbits;
//
////        String headHash = bytesToHex(sha256D(hexStringToByteArray(headStr)));
//        String headHash = bytesToHex(sha256D(hexStringToByteArray("00000020382b11f859c9c16e2c6e9d81426021e4ca97e694efea01000000000000000000bddddb7ef2729a5c2d0ce4e6d567e532467ea9302d798b44bdd3134fab3994ebea380462b48b0a173b283000")));
//
//        System.out.println(headHash);
//
//    }

    @Test
    public void testDataCpuMiner() throws Exception {

        //882560

        String version = toSmallEnd("20000000");
        String prevhash = toSmallEndFor64Hex("6c591e1944ca898c871fe692cc2ed7960a5cf6b90001cf370000000000000000");

        String [] merkle_branch = {"a95accee6ab74935de5ecdaf1744a52f9f6c9e9354fa94e85b18efa80ba4779c","7f8721ca267c52251efb551c2eabc56d7a78bb02a4c47068a3f141a41869593f","0775e2720b863304456edb2716f0e90674ed0bb047f656238dc4f3e5e264eb27","3051fe3e470844ffafde36bb1e1d15e532672a79f101ebea1f7698ba3ebfe783","4e22fcb42d30b6a6b11075e74bdd6b8768f8e209c60dd0ccfc393edeb83f40d4","0211035211377c79eb69a8a864317452931ed4a5997c05637e40fb8dfedb3d1b","dbd1151cd5354dfc4c4f479c9120d96279312780a2447bad88fca06fc469ce44","f3dbcea72f7d48c726d830af34fdc5d72b3db94b073bf1ce1fed08a6352cb7b1","11a30300738b763544b11fa71a8bca1a833f9177d2ce3a4322563e82d69c6ffb","b77bd246f0ae930e342b483a6afee97f18515b7c223aa249e5e6e04e3d819b0c","feb95ab48ebbced00f3e260fdcca1703182c05015620f7eef24a37a5128cd318"};
        String ntime = toSmallEnd("67a6c2ca");
        String nonce = toSmallEnd("4ec70b01");
        String nbits = toSmallEnd("17029a8a");

        String coinb1 = "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff17037b780d5075626c69632d506f6f6c";
        String extranonce1 = "f7a950ca";
        String extranonce2 = "00000000";
        String coinb2 = "ffffffff028630b01200000000160014fe255e4cc6748570c369c2a71640280958d2376e0000000000000000266a24aa21a9edda784229219a40771e1e633cdb9031800dc0140b9826d0b3e2bc0c9f3ff8dfe200000000";

        String coinbase = coinb1 + extranonce1 + extranonce2 + coinb2;
        String merkleRoot = getMerkle_root00(coinbase, merkle_branch);
        String headStr = version + prevhash + merkleRoot + ntime + nbits + nonce;
        String headHash = bytesToHex(sha256D(hexStringToByteArray(headStr)));
        System.out.println(headHash);

    }

    @Test
    public void testDataCpuMiner2() throws Exception {

        String version = toSmallEnd("20000000");
        String prevhash = toSmallEndFor64Hex("68cf846acf4604aef0c27182aefe07f23b0d9008000292360000000000000000");

        String [] merkle_branch = {"168f12f2f8988315ae7e853edf5e033d143beb5877f5c116ee926ea58b8d2f29","7e534107bc5d7e52061c693baed5eac9773dc180b923a422458905e67db602b8","31a6fdee3986011e31d001647120a982de37fe3f3b5eac3aff6830dd013d6439","756697b4c023b8e58f00766b5c316aa447dfe9a9c9a79c09077a7c31915e9c1f","f2d789a0199c521728b5ab44e8ee652144566139198d3eaf49f9424e6e17b567","4fbe810e7cb61a6a2c41cb52b32bb8947bcbe7a400c001a3e46a7960d9f9944f","68ebdb8245594c6c775374ea5a57fe42d8a4d06ad0701d3baf3ea202c2aa2e21","6b98053fc082264a0bffea3f4b7a148e5323900e7d1070b0fde3fdcf215cf982","e865d2f3b1a149f9d4273984d09bd1609c6855365d919b1957df86b2c7b12635","2d1a7e770a6378989279605b9e5670dd60f9da4772f84a4a968dfbe37098da72","63a1555eabd37a63f078858f7eb390a4828bb6472915ce2cf1cdb5b0f8288a4b"};
        String ntime = toSmallEnd("67a6c9b2");
        String nonce = toSmallEnd("b11f21c0");
        String nbits = toSmallEnd("17029a8a");

        String coinb1 = "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff170380780d5075626c69632d506f6f6c";
        String extranonce1 = "43eafbc8";
        String extranonce2 = "00000000";
        String coinb2 = "ffffffff02c75ebf1200000000160014fe255e4cc6748570c369c2a71640280958d2376e0000000000000000266a24aa21a9ede3fe24023aac7849d271005e4a3d732a42fd40997653f0801046f982de7fdd7100000000";

        String coinbase = coinb1 + extranonce1 + extranonce2 + coinb2;
        String merkleRoot = getMerkle_root00(coinbase, merkle_branch);
        String headStr = version + prevhash + merkleRoot + ntime + nbits + nonce;
        String headHash = bytesToHex(sha256D(hexStringToByteArray(headStr)));
        System.out.println(headHash);

    }

    @Test
    public void testByte(){
        String hex1 = "ff";
        byte byte1 = hexStringToByteArray(hex1)[0];
        int unsigned1 = byte1 & 0xFF;
        System.out.println(unsigned1);
        String hex2 = "00";
        byte byte2 = hexStringToByteArray(hex2)[0];
        int unsigned2 = byte2 & 0xFF;
        System.out.println(unsigned2);
        System.out.println(unsigned2<unsigned1);
    }

    @Test
    public void nbitsToTarget() {

    }

    @Test
    public void handlePre(){
        String prevhash = "688d43d32bd00ee6a2496643061af3210b3379ba10d21373abe4e48000000000";
        String res = toSmallEndFor64Hex(prevhash);
    }

    @Test
    public void testToSmallEnd(){
        String prevhash = "688d43d32bd00ee6a2496643061af3210b3379ba10d21373abe4e48000000000";
        String res = toSmallEnd(prevhash);
    }

    @Test
    public void calculateTarget() {

        int nBits = 0x17029a8a;
//        int nBits = 0x8a9a0217;
        // 提取指数和系数

        String nbitsHex = "17029a8a";
        byte [] nbitsByte = hexStringToByteArray(nbitsHex);

        int exponent = (nBits >> 24) & 0xff;  // nBits的高8位是指数
        int coefficient = nBits & 0xffffff;    // nBits的低24位是系数

        // 计算目标值：target = coefficient * 256^(exponent - 3)
        BigInteger target = BigInteger.valueOf(coefficient);
        target = target.multiply(BigInteger.valueOf(256).pow(exponent - 3));

        System.out.println(target.toString(16));
    }

    @Test
    public void calculateTargetByDifficulty() {

        // 给定 difficulty
        double difficulty = 0.0000001;

        // 创世区块的初始目标值
        String targetInitialHex = "00000000FFFF0000000000000000000000000000000000000000000000000000";
        BigInteger targetInitial = new BigInteger(targetInitialHex, 16);

        // 使用 BigDecimal 进行浮动值的精确计算
        BigDecimal difficultyDecimal = new BigDecimal(difficulty);

        // 计算目标值
        BigDecimal targetDecimal = new BigDecimal(targetInitial).divide(difficultyDecimal, 0, BigDecimal.ROUND_DOWN);

        // 将计算后的目标值转换为 BigInteger，以便输出十六进制表示
        BigInteger target = targetDecimal.toBigInteger();

        // 输出计算出的目标值（十六进制字符串）
        String targetHex = target.toString(16);

        // 如果十六进制字符串不足64位，前面补零
        while (targetHex.length() < 64) {
            targetHex = "0" + targetHex;
        }

        System.out.println("Target for difficulty " + difficulty + ": " + targetHex);  // 输出目标值（十六进制）
    }

    @Test
    public void testBlock() throws Exception {

        String version = toSmallEnd("2000e000");
        String prevhash = toSmallEnd("000000000000000000011b0a115e64b06ebd3c35cd8f0ad440aa7dbccc4c634d");

        String ntime = toSmallEnd("67a56920");
        String nonce = toSmallEnd("725ee013");
        String nbits = toSmallEnd("17029a8a");

        String merkle_root = toSmallEnd("c5ea4619e26b587a948847d27184d34d4732118a6841bbb91e5a2049033ae96c");

        String headStr = version + prevhash + merkle_root + ntime + nbits + nonce;

        String headHash = bytesToHex(sha256D(hexStringToByteArray(headStr)));

        System.out.println(headHash);

    }

    @Test
    public void testTime(){
        String dateString = "07 2月 2025 02:00:00 UTC";

        // 定义日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // 设置时区为UTC

        try {
            // 解析日期字符串
            Date date = sdf.parse(dateString);

            // 转换为时间戳（毫秒）
            long timestamp = date.getTime();

            System.out.println("时间戳（毫秒）: " + timestamp);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    public static String toSmallEnd(String input) {

        if(input.length()%2!=0){
            return "";
        }
        StringBuilder output = new StringBuilder();
        for(int i = input.length()-2; i >= 0; i-=2) {
            output.append(input.charAt(i));
            output.append(input.charAt(i+1));
        }
        return output.toString();


    }

    public static String toSmallEndFor64Hex(String input) {
        if(input.length()%8!=0){
            return "";
        }
        StringBuilder output = new StringBuilder();
        for(int i = input.length()-8; i >= 0; i-=8) {
            for (int j = 0; j < 8; j++) {
                output.append(input.charAt(i+j));
            }
        }
        String bigEnd = output.toString();
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static List<String> getMerkleRootList(String coinbase, String[] merkle_branch) throws Exception {
        List<String> res = new ArrayList<>();
//        res.add(getMerkle_root0(coinbase, merkle_branch));
        res.add(getMerkle_root00(coinbase, merkle_branch));
//        res.add(getMerkle_root1(coinbase, merkle_branch));
//        res.add(getMerkle_root11(coinbase, merkle_branch));
//        res.add(getMerkle_root2(coinbase, merkle_branch));
//        res.add(getMerkle_root22(coinbase, merkle_branch));
//        res.add(getMerkle_root0r(coinbase, merkle_branch));
//        res.add(getMerkle_root00r(coinbase, merkle_branch));
//        res.add(getMerkle_root1r(coinbase, merkle_branch));
//        res.add(getMerkle_root11r(coinbase, merkle_branch));
//        res.add(getMerkle_root2r(coinbase, merkle_branch));
//        res.add(getMerkle_root22r(coinbase, merkle_branch));
        return res;
    }

    public static String getMerkle_root0(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for (int i = merkle_branch.length-1; i >= 0; i--) {
            String s = merkle_branch[i];
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(s);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    //这个是对的
    public static String getMerkle_root00(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for(String s : merkle_branch){
            byte[] sByte = hexStringToByteArray(s);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }

        String bigEnd = bytesToHex(res);
        return bigEnd;
    }

    public static String getMerkle_root1(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for (int i = merkle_branch.length-1; i >= 0; i--) {
            String s = merkle_branch[i];
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root11(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for(String s : merkle_branch){
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root2(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for (int i = merkle_branch.length-1; i >= 0; i--) {
            String s = merkle_branch[i];
            String sSmallEnd = toSmallEnd(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root22(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for(String s : merkle_branch){
            String sSmallEnd = toSmallEnd(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root0r(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for (int i = merkle_branch.length-1; i >= 0; i--) {
            String s = merkle_branch[i];
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(s);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256Drevers(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root00r(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for(String s : merkle_branch){
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(s);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256Drevers(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root1r(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for (int i = merkle_branch.length-1; i >= 0; i--) {
            String s = merkle_branch[i];
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256Drevers(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root11r(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for(String s : merkle_branch){
            String sSmallEnd = toSmallEndFor64Hex(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256Drevers(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root2r(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for (int i = merkle_branch.length-1; i >= 0; i--) {
            String s = merkle_branch[i];
            String sSmallEnd = toSmallEnd(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256Drevers(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    public static String getMerkle_root22r(String coinbase, String[] merkle_branch) throws Exception {

        byte [] res = sha256D(hexStringToByteArray(coinbase));

        for(String s : merkle_branch){
            String sSmallEnd = toSmallEnd(s);
            byte[] sByte = hexStringToByteArray(sSmallEnd);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256Drevers(temp);
        }

        String bigEnd = bytesToHex(res);
        String smallEnd = toSmallEnd(bigEnd);
        return smallEnd;
    }

    private static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    private static byte[] sha256(byte[] input) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        return digest.digest(input);
    }

    private static byte[] sha256D(byte[] input) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] res1 = digest.digest(input);
        byte[] res2 = digest.digest(res1);
        return res2;
    }

    private static byte[] sha256Drevers(byte[] input) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] res1 = digest.digest(input);
        byte[] res2 = digest.digest(res1);
        String s = bytesToHex(res2);
        String smallEnd = toSmallEnd(s);
        return hexStringToByteArray(smallEnd);
    }
}
